package com.wontube.core.pub.servlet.handlers;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.ibatis.session.SqlSession;

import com.wontube.core.annotation.AutoTransSupport;
import com.wontube.core.dao.DatasourceContext;
import com.wontube.core.plugin.IPlugin;
import com.wontube.core.pub.AppEnvironment;
import com.wontube.core.pub.ServerContext;
import com.wontube.core.remote.IHandler;


/**
 * @author yuechao
 * 
 */
public abstract class AbstractHander implements IHandler {
	protected final Logger logger = LoggerFactory.getLogger(this.getClass());

	/**
	 * 请求上下文线程变量
	 * 
	 */
	protected ThreadLocal<ServerContext> __context = new ThreadLocal<ServerContext>();

	/**
	 * 方法缓存
	 * 
	 */
	private final HashMap<MethodKey, MethodWrapper> methodCache = new HashMap<MethodKey, MethodWrapper>(8);

	/**
	 * 取得上下文环境， 要求线程间独立
	 * 
	 * @return
	 */
	public final ServerContext getContext() {
		ServerContext context = this.__context.get();
		if (context == null) {
			context = new ServerContext();
			this.__context.set(context);
		}
		return context;
	}

	/**
	 * 设置上下文环境
	 * 
	 * @param value
	 */
	public final void setContext(ServerContext value) {
		this.__context.set(value);
	}

	/**
	 * 处理请求前，执行插件动作。如果有错误，则返回false，
	 * 
	 * 并将错误信息放在context.errMessage 字段中
	 * 
	 * @throws Exception
	 * 
	 */
	public final boolean begin(HandlerConfig config) throws Exception {
		if (config.isAutoTrans()) {
			SqlSession sms = this.getSqlSession(!config.isAutoTrans());
			if (sms != null) {
				DatasourceContext.setTrans(sms);
			}
		}

		for (IPlugin obj : AppEnvironment.pluginList) {
			if (!obj.begin(this)) {
				return false;
			}
		}

		return true;
	}

	/**
	 * 处理请求成功后，执行插件动作
	 * 
	 */
	public final void commit(HandlerConfig config) throws Exception {
		for (int i = AppEnvironment.pluginList.size() - 1; i >= 0; i--) {
			AppEnvironment.pluginList.get(i).commit(this);
		}

		if (config.isAutoTrans()) {
			SqlSession sms = this.getSqlSession(!config.isAutoTrans());
			if (sms != null) {
				sms.commit();
			}
		}
	}

	/**
	 * 处理请求完毕后，执行资源清理
	 * 
	 */
	public final void end(HandlerConfig config) {
		for (int i = AppEnvironment.pluginList.size() - 1; i >= 0; i--) {
			AppEnvironment.pluginList.get(i).end(this);
		}

		if (config.isAutoTrans()) {
			SqlSession sms;
			try {
				sms = this.getSqlSession(!config.isAutoTrans());
				sms.close();
			} catch (SQLException e) {
				logger.error(e.toString());
			}
		}

		this.destroy();
	}

	/**
	 * 取得SqlSession，根据需要操作的数据库不同，
	 * 
	 * 返回不同的值, 如果不需要数据库操作，返回 null
	 * 
	 * @param autoCommit
	 * @return
	 * @throws SQLException
	 * 
	 */
	public abstract SqlSession getSqlSession(boolean autoCommit) throws SQLException;

	/**
	 * 根据方法名以及参数长度，得到方法，以支持方法重载
	 * 
	 * @param methodName
	 * @param argSize
	 * @return
	 */
	@Override
	public final MethodWrapper getMethod(String methodName, int argSize) {
		MethodKey key = new MethodKey(methodName, argSize);
		if (this.methodCache.containsKey(key)) {
			return this.methodCache.get(key);
		}

		Class<?> clazz = this.getClass();
		Method[] methodList = clazz.getDeclaredMethods();

		for (Method method : methodList) {
			if (method.getName().equalsIgnoreCase(methodName)) {
				Class<?>[] clazzTypeList = method.getParameterTypes();
				if (clazzTypeList.length == argSize) {
					AutoTransSupport support = method.getAnnotation(AutoTransSupport.class);
					if (support == null) { // 如果方法未声明，看类型
						support = clazz.getAnnotation(AutoTransSupport.class);
					}
					boolean isAutoTrans = support != null;
					int transIsolation = isAutoTrans ? support.transIsolation() : Connection.TRANSACTION_READ_COMMITTED;
					HandlerConfig config = new HandlerConfig(this, isAutoTrans, transIsolation);
					MethodWrapper wapper = new MethodWrapper(config, method);

					// 缓存
					this.methodCache.put(new MethodKey(methodName, argSize), wapper);

					return wapper;
				}
			}
		}

		return null;
	}

	/**
	 * 请求完成后清理资源
	 * 
	 * @throws SQLException
	 * 
	 */
	protected void destroy() {
		DatasourceContext.close();
		this.__context.remove();
	}
}

/**
 * @author yuechao
 * 
 *         2011-5-28 上午09:02:22
 * 
 */
class MethodKey {
	private String methodName = "";
	private int argSize = 0;

	/**
	 * @param methodName
	 * @param argSize
	 */
	public MethodKey(String methodName, int argSize) {
		this.methodName = methodName;
		this.argSize = argSize;
	}

	@Override
	public boolean equals(Object obj) {
		if (super.equals(obj)) {
			return true;
		}

		if (!(obj instanceof MethodKey)) {
			return false;
		}

		MethodKey other = (MethodKey) obj;
		return this.methodName.equals(other.methodName) && this.argSize == other.argSize;
	}

	@Override
	public int hashCode() {
		return this.methodName.hashCode() + this.argSize;
	}
}
